import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_diabetes
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
import seaborn as sns
import pandas as pd

import matplotlib.pyplot as plt
from matplotlib import rcParams
from matplotlib.font_manager import FontProperties
# 设置字体为 SimHei（黑体）
plt.rcParams['font.sans-serif'] = ['Songti SC']  # 用黑体显示中文
plt.rcParams['axes.unicode_minus'] = False   # 解决负号 '-' 显示为方块的问题

data_set = load_diabetes()
x = pd.DataFrame(data_set.data, columns=data_set.feature_names)
y = pd.Series(data_set.target, name='target')

plt.figure(figsize=(15, 6))
for i , col in enumerate(x.columns):
    plt.subplot(2, 5 , i +1)
    plt.hist(x[col] , bins = 30 ,color='green')
    plt.title(col)
    plt.xlabel(col)
    #plt.ylabel('病情发展指数 (Progression)')
plt.tight_layout()
plt.show()

x_bmi = x.bmi
plt.figure(figsize=(10,5))
for i , col in enumerate(('bmi','bp')):
    plt.subplot(1,2,i+1)
    plt.scatter(x[col] , y ,color = 'blue')
    plt.xlabel(col)
    plt.ylabel('病情发展指数 (Progression)')
plt.tight_layout()
plt.show()

def divide_data(data_in_x ,data_in_y ,test_size = 0.2 ,random_seed = None):
    if random_seed is not None:
        np.random.seed(random_seed)
    indices = np.random.permutation(len(data_in_x))
    point = int(len(data_in_x) * test_size)
    data_out_index = {"test" : indices[:point] , "train" : indices[point:]}
    x_train , x_test = [],[]
    y_train , y_test = [],[]
    x_test , x_train = (data_in_x.iloc[data_out_index['test']] , data_in_x.iloc[data_out_index['train']] )\
        if isinstance(data_in_x, pd.DataFrame) else (data_in_x[data_out_index['test']],data_in_x[data_out_index['train']])
    y_test , y_train = (data_in_y.iloc[data_out_index['test']],data_in_y.iloc[data_out_index['train']] )\
        if isinstance(data_in_y, pd.DataFrame) else (data_in_y[data_out_index['test']],data_in_y[data_out_index['train']])
    return {'test' :x_test , 'train' :x_train } , {'test':y_test ,'train':y_train}#或许我应该将其作为表格一起处理的

def liner_regression(x_data, y_data):
    model = LinearRegression()
    if isinstance(x_data, pd.DataFrame):
        model.fit(np.array(x_data), y_data)
        # coef_ 可能有多维，直接打印数组即可
        print(f"斜率(slope){model.coef_}, 截距(intercept){model.intercept_}")
    else:
        model.fit(np.array(x_data).reshape(-1,1), y_data)
        print(f"斜率(slope){model.coef_[0]}, 截距(intercept){model.intercept_}")
    slope = model.coef_
    intercept = model.intercept_
    return slope,intercept

def image_create(data_x, data_y, slope, intercept, factor='Unknown'):
    plt.figure(figsize=(10, 6))
    plt.scatter(data_x['train'], data_y['train'], color='blue', alpha=0.7, label='train data')
    plt.scatter(data_x['test'], data_y['test'], color='red', alpha=0.7, label='test data')
    plt.axline((0, intercept), slope=slope[0], color='red', linewidth=2)
    plt.title(f'y = {slope[0]:.2f}*x + {intercept:.2f}')
    plt.xlabel(factor)
    plt.ylabel('病情发展指数 (Progression)')
    plt.legend()
    plt.show()
    return True

def error_image(x_data, y_data, slope, intercept, test_or_train=True):
    # Select data based on flag
    key = 'test' if test_or_train else 'train'
    X = np.array(x_data[key])
    y_true = np.array(y_data[key])
    # Compute predictions for 1D or multi-D features
    if X.ndim == 1:
        y_pred = X * slope[0] + intercept
    else:
        y_pred = np.dot(X, slope) + intercept
    # Calculate residuals
    error = y_true - y_pred
    # Plot residual histogram
    plt.figure(figsize=(10, 6))
    plt.hist(error, bins=20, color='blue')
    plt.title('残差图')
    plt.xlabel('Residual')
    plt.ylabel('Frequency')
    plt.show()

def show_mse(x_data , y_data , picture_line = False , picture_error = True , test_or_train = False):
    x_div , y_div = divide_data(x_data , y_data ,random_seed=31459)
    slope , interccept = liner_regression(x_div['train'] , y_div['train'])
    #prediction 
    x_pre = np.array(x_div['test'])
    if x_pre.ndim == 1:
        x_pre = x_pre.reshape(-1,1)
    y_pre = np.dot(x_pre,slope) + interccept
    x_pre.reshape(1,-1)
    mse = mean_squared_error(y_div['test'] , y_pre)
    r2 = r2_score(y_div['test'] , y_pre)
    print(f"均方误差(MSE) : {mse}")
    print(f"r^2 : {r2}")
    if picture_line:
        image_create(x_div , y_div , slope , interccept)
    if picture_error:
        error_image(x_div , y_div , slope , interccept,test_or_train)
    return mse,r2

show_mse(x.bmi, y , picture_line=True)

three_val = pd.concat([x.bmi , x.s1 , x.s2],axis = 1)
show_mse(three_val ,y , picture_error=True)